﻿// Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
//
// This file is part of SharpMap.
// SharpMap is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// SharpMap is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public License
// along with SharpMap; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Text;

namespace Petra.Gis.ReferenceSystemServices
{
  /// <summary>
  /// A coordinate system based on latitude and longitude. 
  /// </summary>
  /// <remarks>
  /// Some geographic coordinate systems are Lat/Lon, and some are Lon/Lat. 
  /// You can find out which this is by examining the axes. You should also 
  /// check the angular units, since not all geographic coordinate systems 
  /// use degrees.
  /// </remarks>
  [DataContract(Name="GEOGSC")]
  [KnownType(typeof(AngularUnit))]
  public class GeographicCoordinateSystem : HorizontalCoordinateSystem
  {


    /// <summary>
    /// xaml constr.
    /// </summary>
    public GeographicCoordinateSystem() {}





    /// <summary>
    /// Creates an instance of a Geographic Coordinate System
    /// </summary>
    /// <param name="horizontalDatum">Horizontal datum</param>
    /// <param name="angularUnit">Angular units</param>
    /// <param name="primeMeridian">Prime meridian</param>
    /// <param name="axes">Axis info</param>
    /// <param name="metadata">Coord. system metadata</param>
    public GeographicCoordinateSystem(HorizontalDatum horizontalDatum, AngularUnit storageUnit, PrimeMeridian primeMeridian, AxisList axes, Metadata metadata)
      :
      base(storageUnit, axes, metadata)
    {
      Datum = horizontalDatum;
      PrimeMeridian = primeMeridian;
    }

    //#region Predefined geographic coordinate systems

    ///// <summary>
    ///// Creates a decimal degrees geographic coordinate system based on the WGS84 ellipsoid, suitable for GPS measurements
    ///// </summary>
    //public static GeographicCoordinateSystem WGS84
    //{
    //  get
    //  {
    //    List<AxisInfo> axes = new List<AxisInfo>(2);
    //    axes.Add(new AxisInfo("Lon", AxisOrientationEnum.East));
    //    axes.Add(new AxisInfo("Lat", AxisOrientationEnum.North));
    //    return new GeographicCoordinateSystem(SharpMap.CoordinateSystems.AngularUnit.Degrees,
    //      SharpMap.CoordinateSystems.HorizontalDatum.WGS84, SharpMap.CoordinateSystems.PrimeMeridian.Greenwich, axes,
    //      "WGS 84", "EPSG", 4326, String.Empty, string.Empty, string.Empty);
    //  }
    //}

    //#endregion

    #region GeographicCoordinateSystem Members




    //private HorizontalDatum _HorizontalDatum;

    /// <summary>
    /// Gets or sets the HorizontalDatum.
    /// </summary>
    [DataMember(Name = "DATUM", Order = 1)]
    public HorizontalDatum Datum
    { get; set; }





    //private PrimeMeridian _PrimeMeridian;

    /// <summary>
    /// Gets or sets the prime meridian of the geographic coordinate system.
    /// </summary>
    [DataMember(Name = "PRIMEM", Order = 2 )]
    public PrimeMeridian PrimeMeridian
    { get; set; }





    /// <summary>
    /// Gets or sets the angular units of the geographic coordinate system.
    /// </summary>
    [DataMember(Name="UNIT", Order = 3)]
    public override MeasureUnit Unit
    {
      get { return base.Unit; }
      set { base.Unit = (AngularUnit)value; }
    }





    //VerticalDatum _verticalDatum;

    //public VerticalDatum VerticalDatum
    //{
    //  get { return _verticalDatum; }
    //  set { _verticalDatum = value; }
    //}



    /// <summary>
    /// Checks whether the values of this instance is equal to the values of another instance.
    /// Only parameters used for coordinate system are used for comparison.
    /// Name, abbreviation, authority, alias and remarks are ignored in the comparison.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns>True if equal</returns>
    public override bool EqualParams(CoordinateSystem obj, CsCompareType compareType)
    {
      if (!(obj is GeographicCoordinateSystem))
        return false;
      GeographicCoordinateSystem gcs = obj as GeographicCoordinateSystem;
      if (gcs.Dimension != this.Dimension) return false;

      if (!obj.Axes.EqualParams(this.Axes, CsCompareType.CompareAllParams))
        return false;

      //if (this.Axes.Count != gcs.Axes.Count) return false;
      //for (int i = 0; i < gcs.Axes.Count; i++)
      //  if (gcs.Axes[i].Orientation != this.Axes[i].Orientation)
      //    return false;

      if (compareType == CsCompareType.SkipStorageUnits || gcs.Unit == null || this.Unit == null)
        return gcs.Datum.EqualParams(this.Datum) &&
            gcs.PrimeMeridian.EqualParams(this.PrimeMeridian);
      else
        return gcs.Unit.EqualParams(this.Unit) &&
          gcs.Datum.EqualParams(this.Datum) &&
          gcs.PrimeMeridian.EqualParams(this.PrimeMeridian);
    }
    #endregion





    /// <summary>
    /// Creates new instance of GeographicCoordinateSystem from existing and replaces original Spheroid
    /// with gauss sphere. Gauss sphere radius is computed from oblique standard parallel.
    /// </summary>
    /// <param name="origGCS">original GeographicCoordinateSystem</param>
    /// <param name="obliqueStandardParallel">standard parallel latitude on ellipsoid, given in radians</param>
    /// <returns>new, pseudo-GeographicCoordinateSystem with gauss sphere instead of ellipsoid</returns>
    public static GeographicCoordinateSystem CreateGaussSphereCS(
      GeographicCoordinateSystem origGCS,
      double paramObliqueStandardParallel)
    {

      if (paramObliqueStandardParallel < 0 || paramObliqueStandardParallel > (Math.PI / 2.0))
        throw new ArgumentOutOfRangeException("oblique standard parallel must be from range 0 - 90 deg (use proper units !)");

      double radius = origGCS.Datum.Ellipsoid.MeanCurvatureRadius(paramObliqueStandardParallel);

      GeographicCoordinateSystem tmp = (GeographicCoordinateSystem)origGCS.Clone();

      tmp.Datum = new HorizontalDatum()
      {
        Metadata = new Metadata("gauss sphere", null, 0, "gauss sphere", null, "spheroid replacement for oblique projections"),
        Ellipsoid = new Spheroid("Gauss", radius, radius, false, origGCS.Datum.Ellipsoid.AxisUnit),
      };
      tmp.Metadata = new Metadata()
      {
        Name = "Gauss sphere geographics coordinate system"
      };

      return tmp;
    }





  }

}
